You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

layout-main.tsx 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect, useMemo, useState } from 'react'
  4. import { usePathname } from 'next/navigation'
  5. import { useTranslation } from 'react-i18next'
  6. import type { RemixiconComponentType } from '@remixicon/react'
  7. import {
  8. RiEqualizer2Fill,
  9. RiEqualizer2Line,
  10. RiFileTextFill,
  11. RiFileTextLine,
  12. RiFocus2Fill,
  13. RiFocus2Line,
  14. } from '@remixicon/react'
  15. import AppSideBar from '@/app/components/app-sidebar'
  16. import Loading from '@/app/components/base/loading'
  17. import DatasetDetailContext from '@/context/dataset-detail'
  18. import { DataSourceType } from '@/models/datasets'
  19. import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
  20. import { useStore } from '@/app/components/app/store'
  21. import { useAppContext } from '@/context/app-context'
  22. import { PipelineFill, PipelineLine } from '@/app/components/base/icons/src/vender/pipeline'
  23. import { useDatasetDetail, useDatasetRelatedApps } from '@/service/knowledge/use-dataset'
  24. import useDocumentTitle from '@/hooks/use-document-title'
  25. import ExtraInfo from '@/app/components/datasets/extra-info'
  26. import { useEventEmitterContextContext } from '@/context/event-emitter'
  27. import cn from '@/utils/classnames'
  28. export type IAppDetailLayoutProps = {
  29. children: React.ReactNode
  30. params: { datasetId: string }
  31. }
  32. const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
  33. const {
  34. children,
  35. params: { datasetId },
  36. } = props
  37. const { t } = useTranslation()
  38. const pathname = usePathname()
  39. const hideSideBar = pathname.endsWith('documents/create') || pathname.endsWith('documents/create-from-pipeline')
  40. const isPipelineCanvas = pathname.endsWith('/pipeline')
  41. const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
  42. const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
  43. const { eventEmitter } = useEventEmitterContextContext()
  44. eventEmitter?.useSubscription((v: any) => {
  45. if (v?.type === 'workflow-canvas-maximize')
  46. setHideHeader(v.payload)
  47. })
  48. const { isCurrentWorkspaceDatasetOperator } = useAppContext()
  49. const media = useBreakpoints()
  50. const isMobile = media === MediaType.mobile
  51. const { data: datasetRes, error, refetch: mutateDatasetRes } = useDatasetDetail(datasetId)
  52. const { data: relatedApps } = useDatasetRelatedApps(datasetId)
  53. const isButtonDisabledWithPipeline = useMemo(() => {
  54. if (!datasetRes)
  55. return true
  56. if (datasetRes.provider === 'external')
  57. return false
  58. if (datasetRes.runtime_mode === 'general')
  59. return false
  60. return !datasetRes.is_published
  61. }, [datasetRes])
  62. const navigation = useMemo(() => {
  63. const baseNavigation = [
  64. {
  65. name: t('common.datasetMenus.hitTesting'),
  66. href: `/datasets/${datasetId}/hitTesting`,
  67. icon: RiFocus2Line,
  68. selectedIcon: RiFocus2Fill,
  69. disabled: isButtonDisabledWithPipeline,
  70. },
  71. {
  72. name: t('common.datasetMenus.settings'),
  73. href: `/datasets/${datasetId}/settings`,
  74. icon: RiEqualizer2Line,
  75. selectedIcon: RiEqualizer2Fill,
  76. disabled: false,
  77. },
  78. ]
  79. if (datasetRes?.provider !== 'external') {
  80. baseNavigation.unshift({
  81. name: t('common.datasetMenus.pipeline'),
  82. href: `/datasets/${datasetId}/pipeline`,
  83. icon: PipelineLine as RemixiconComponentType,
  84. selectedIcon: PipelineFill as RemixiconComponentType,
  85. disabled: false,
  86. })
  87. baseNavigation.unshift({
  88. name: t('common.datasetMenus.documents'),
  89. href: `/datasets/${datasetId}/documents`,
  90. icon: RiFileTextLine,
  91. selectedIcon: RiFileTextFill,
  92. disabled: isButtonDisabledWithPipeline,
  93. })
  94. }
  95. return baseNavigation
  96. }, [t, datasetId, isButtonDisabledWithPipeline, datasetRes?.provider])
  97. useDocumentTitle(datasetRes?.name || t('common.menus.datasets'))
  98. const setAppSidebarExpand = useStore(state => state.setAppSidebarExpand)
  99. useEffect(() => {
  100. const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
  101. const mode = isMobile ? 'collapse' : 'expand'
  102. setAppSidebarExpand(isMobile ? mode : localeMode)
  103. }, [isMobile, setAppSidebarExpand])
  104. if (!datasetRes && !error)
  105. return <Loading type='app' />
  106. return (
  107. <div
  108. className={cn(
  109. 'flex grow overflow-hidden',
  110. hideHeader && isPipelineCanvas ? '' : 'rounded-t-2xl border-t border-effects-highlight',
  111. )}
  112. >
  113. <DatasetDetailContext.Provider value={{
  114. indexingTechnique: datasetRes?.indexing_technique,
  115. dataset: datasetRes,
  116. mutateDatasetRes,
  117. }}>
  118. {!hideSideBar && (
  119. <AppSideBar
  120. navigation={navigation}
  121. extraInfo={
  122. !isCurrentWorkspaceDatasetOperator
  123. ? mode => <ExtraInfo relatedApps={relatedApps} expand={mode === 'expand'} documentCount={datasetRes?.document_count} />
  124. : undefined
  125. }
  126. iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
  127. />
  128. )}
  129. <div className='grow overflow-hidden bg-background-default-subtle'>{children}</div>
  130. </DatasetDetailContext.Provider>
  131. </div>
  132. )
  133. }
  134. export default React.memo(DatasetDetailLayout)